/**
 * 泛型（Generics）是一种编程语言特性，允许在定义函数、类、接口等时使用占位符来表示类型，而不是具体的类型。
 * 泛型是一种在编写可重用、灵活且类型安全的代码时非常有用的功能。
 * 使用泛型的主要目的是为了处理不特定类型的数据，使得代码可以适用于多种数据类型而不失去类型检查。
 *
 * 泛型的优势包括：
 * 代码重用： 可以编写与特定类型无关的通用代码，提高代码的复用性。
 * 类型安全： 在编译时进行类型检查，避免在运行时出现类型错误。
 * 抽象性： 允许编写更抽象和通用的代码，适应不同的数据类型和数据结构。
 *
 * 泛型标识符
 * 在泛型中，通常使用一些约定俗成的标识符，比如常见的 T（表示 Type）、U、V 等，但实际上你可以使用任何标识符。
 *
 */

//T: 代表 "Type"，是最常见的泛型类型参数名。
function generics<T>( arg : T ) : T {
    return arg;
}

let output = generics<string>( "myString" );
console.log( output );
let output2 = generics<number>( 100 );
console.log( output2 );


// K, V: 代表 "Key" 和 "Value"，分别表示键和值的类型。
interface KeyValue<K, V> {
    key : K;
    value : V;
}

let kv1 : KeyValue<number, string> = {
    key : 1,
    value : "Steve"
};
console.log( kv1 );

// E: 用于表示数组元素的泛型类型参数。
function printArray<E>( arr : E[] ) : E[] {
    return arr.filter( item => {
        return item;
    } );
}

let arrG = printArray<string | number>( [ "a", "b", "c", 0, "" ] );
console.log( arrG );

// R: 用于表示函数返回值的泛型类型参数。
function getResult<R>( result : R ) : R {
    return result;
}
let resultG = getResult<string>( "success" );
console.log( resultG );

// U, V: 通常用于表示第二、第三个泛型类型参数
function combine<U, V>( first : U, second : V ) :string {
    return `${ first } ${ second }`;
}
let combineG = combine<string, number>( "Steve", 1 );
console.log( combineG );

/**
 * 这些标识符是约定俗成的，实际上你可以选择任何符合标识符规范的名称。
 * 关键是使得代码易读和易于理解，所以建议在泛型类型参数上使用描述性的名称，以便于理解其用途。
 */


// 泛型接口
interface Pair<T, U> {
    first : T;
    second : U;
}
let pair : Pair<string, number> = { first : "hello", second : 42 };
console.log( pair );// { first: 'hello', second: 42 }
let pair2 : Pair<number, string> = { first : 41, second : "world" };
console.log( pair2 );// { first: 41, second: 'world' }

// 泛型类
class GenericClass<T> {
    private _value : T;
    constructor( value : T ) {
        this._value = value;
    }
    getValue() : T {
        return this._value;
    }
    setValue( value : T ) {
        this._value = value;
    }
}
let genericClass = new GenericClass<string>( "hello" );
genericClass.setValue( "hello world" );
console.log( genericClass.getValue() );

// 泛型约束
interface LInterface {
    length : number;
}
function logLength<T extends LInterface>( arg: T ):void {
    console.log( arg.length );
}
let demoNum = 100000;
logLength( "hello");// 5
logLength( [ 1, 2, 3 ] );// 3
logLength( demoNum.toString() );

// 泛型默认类型
function defaultValue<T = string>( arg: T ): T {
    return arg;
}
let defaultValueG = defaultValue("hello");
let defaultValueG2 = defaultValue(42);
console.log( defaultValueG, defaultValueG2 );